/*
 * Copyright 2000 the original author or authors.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "WtInitCmds.h"
#include "WtSettings.h"
#include "WtUtil.h"
#include "WtWebErrors.h"
#include "WtTable.h"
#include "WtTableUtil.h"
#include "WtTableCmds.h"
#include "WtExecute.h"
#include "WtServerCmds.h"
#include "WtBasicCmds.h"
#include "WtClientRequest.h"
#include "WtResponse.h"
#include "WtUpload.h"
#include "WtCookie.h"
#include "WtDbSession.h"
#include "WtContextEvents.h"

int WtInitInterpCmdInternal(Tcl_Obj *const objv[], int objc, Tcl_Interp *interp)
{
  int ok = 1, i, initRequest = 0;
  WtContext *w = WtGetAssocContext(interp);
  Tcl_Obj *args[4];

  const char *namespaceList[] = {
    "::wt::",
    "::wt::internal::",
    "::wt::server::",
    "::wt::page::",
    "::wt::request::",
    "::wt::response::",
    "::wt::web::",
    "::wt::session::",
    "::wt::util::"
  };

  if (Tcl_DeleteCommand(interp, Tcl_GetString(objv[0])) != 0) {
    WtInterpError(HERE, w, interp);
    ok = 0;
  }

  if (ok) {
    if (Tcl_GetBooleanFromObj(interp, objv[1], &initRequest) != TCL_OK) {
      WtInterpError(HERE, w, interp);
      ok = 0;
    } else if (initRequest) {
      w->web->interpRequestCount++;
      w->web->deleteInterp = !w->web->isPersistent;
      if (!WtInitCtxEventHandlers(w, interp)) {
        WtInterpError(HERE, w, interp);
        ok = 0;
      }
    }
  }

  if (ok && w->web->interpIsNew) {
    /* Eval the interp init script */

    if (!WtEvalEventHandlers(&w->web->ctxEventHandlers, "initInterp",
        interp, w)) {
      WtInterpError(HERE, w, interp);
      ok = 0;
    }

    /* Create the command namespaces */

    if (ok) {
      for (i = 0; i < sizeof(namespaceList) / sizeof(const char *); ++i) {
        args[0] = WtNewString("namespace");
        args[1] = WtNewString("eval");
        args[2] = WtNewString(namespaceList[i]);
        args[3] = WtNewString(NULL);

        if (WtEvalIncr(interp, 4, args, TCL_EVAL_DIRECT) != TCL_OK) {
          WtInterpError(HERE, w, interp);
          ok = 0;
          break;
        }
      }
    }

    if (ok && !WtInitTaskObjectsB(w)) {
      WtInterpError(HERE, w, interp);
      ok = 0;
    }

    if (ok) {
      /* Bind the commands */

      WtInitTableCommands(interp);
      WtInitPageCommands(interp);
      WtInitServerCommands(interp);
      WtInitBasicCommands(interp);
      WtInitClientRequestCommands(interp);
      WtInitResponseCommands(interp);
      WtInitUploadCommands(interp);
      WtInitCookieCommands(interp);
      WtInitSessionCommands(interp);
      WtInitDbSessionCommands(interp);

      /* The wt::core package will be present if the module
         is initialized */

      if (Tcl_PkgProvide(interp, "wt::core", "1") != TCL_OK) {
        WtInterpError(HERE, w, interp);
        ok = 0;
      }
    }
  }

  Tcl_SetObjResult(interp, WtNewBool(ok));

  return TCL_OK;
}

int WtInitPackagesCmdInternal(Tcl_Obj *const objv[], int objc, Tcl_Interp *interp)
{
  int ok = 1, i, initPackages = 0;
  WtContext *w = WtGetAssocContext(interp);
  Tcl_Obj *importCmd[3], *boolObj, *regCmd[3];

  const char *packageList[] = {
    "wt",
    "wt::server",
    "wt::page",
    "wt::request",
    "wt::response",
    "wt::web",
    "wt::session",
    "wt::util",
    "wt::contextInfo",
    "wt::database::dbManager",
    "wt::session::dbSession::odbcSession::sqlServerSession",
    "wt::server::setup"
  };

  const char *patternList[] = {
    "::wt::*",
    "::wt::server::*",
    "::wt::page::*",
    "::wt::request::*",
    "::wt::response::*",
    "::wt::web::*",
    "::wt::session::*",
    "::wt::util::*",
    "::wt::contextInfo::*",
    "::wt::database::dbManager::*",
    "::wt::server::setup::*"
  };

  if (Tcl_DeleteCommand(interp, Tcl_GetString(objv[0])) != 0) {
    ok = 0;
  }

  /* Load the packages */

  if (ok) {
    if (!w->web->packageLoadDone) {
      initPackages = 1;
    }
    for (i = 0; i < sizeof(packageList) / sizeof(char *); ++i) {
      if (Tcl_PkgRequire(interp, packageList[i], NULL, 0) == NULL) {
        ok = 0;
        break;
      }
    }

    if (ok) {
      w->web->packageLoadDone = 1;
    }
  }

  /* Import the core commands */

  if (ok && w->web->importCommands) {
    if (!WtOwnTable(&w->web->usedNamespaces, interp)) {
      ok = 0;
    } else if (!WtTableHas(w->web->usedNamespaces, w->web->taskNamespace)) {
      for (i = 0; i < sizeof(patternList) / sizeof(char *); ++i) {
        importCmd[0] = WtNewString("namespace");
        importCmd[1] = WtNewString("import");
        importCmd[2] = WtNewString(patternList[i]);

        if (WtEvalIncr(interp, 3, importCmd, 0) != TCL_OK) {
          ok = 0;
          break;
        }
      }

      if (ok) {
        boolObj = WtNewBool(1);
        Tcl_IncrRefCount(boolObj);
        WtTableSet(w->web->usedNamespaces, w->web->taskNamespace,
            boolObj);
        Tcl_DecrRefCount(boolObj);
      }
    }
  }

  /* Init settings */

  if (ok) {
    if (!WtConvertToTable(w->web->taskSettings, interp)) {
      ok = 0;
    } else if (!WtInitWebSettings(w, w->web->taskSettings, interp)) {
      ok = 0;
    }
  }

  /* Load packages script */

  if (ok && initPackages) {
    if (!WtEvalEventHandlers(&w->web->ctxEventHandlers, "loadPackages",
        interp, w)) {
      ok = 0;
    }
  }

  /* Init DB settings */

  if (ok && Tcl_GetCharLength(w->web->dataSourceHandler)) {
    regCmd[0] = WtNewString("::wt::database::dbManager::registerDataSource");
    regCmd[1] = WtNewString("wtDb");
    regCmd[2] = w->web->dataSourceHandler;
    if (WtEvalIncr(interp, 3, regCmd, 0) != TCL_OK) {
      ok = 0;
    }
  }

  if (!ok) {
    WtInterpError(HERE, w, interp);
  }

  Tcl_SetObjResult(interp, WtNewBool(ok));

  return TCL_OK;
}

int WtStartRequestCmdInternal(Tcl_Obj *const objv[], int objc, Tcl_Interp *interp)
{
  int ok = 1;
  WtContext *w = WtGetAssocContext(interp);

  if (Tcl_DeleteCommand(interp, Tcl_GetString(objv[0])) != 0) {
    ok = 0;
  }

  /* Start worker script */

  if (ok && w->requestCount == 1) {
    if (!WtEvalEventHandlers(&w->web->ctxEventHandlers, "startWorker",
        interp, w)) {
      ok = 0;
    }
  }

  /* Create interp script */

  if (ok && w->web->interpIsNew) {
    if (!WtEvalEventHandlers(&w->web->ctxEventHandlers, "createInterp",
        interp, w)) {
      ok = 0;
    }
  }

  /* Start request script */

  if (ok) {
    if (!WtEvalEventHandlers(&w->web->ctxEventHandlers, "startRequest",
        interp, w)) {
      ok = 0;
    }
  }

  if (!ok) {
    WtInterpError(HERE, w, interp);
  }

  Tcl_SetObjResult(interp, WtNewBool(ok));

  return ok;
}

int WtEndRequestCmdInternal(Tcl_Obj *const objv[], int objc, Tcl_Interp *interp)
{
  int ok = 1;
  WtContext *w = WtGetAssocContext(interp);

  if (Tcl_DeleteCommand(interp, Tcl_GetString(objv[0])) != 0) {
    WtInterpError(HERE, w, w->web->interp);
    ok = 0;
  }

  /* End request script */

  if (!WtEvalEventHandlers(&w->web->ctxEventHandlers, "endRequest",
      interp, w)) {
    WtInterpError(HERE, w, w->web->interp);
    ok = 0;
  }

  /* Finalize the session */

  if (!WtEvalEventHandlers(&w->web->ctxEventHandlers, "endSession",
      interp, w)) {
    WtInterpError(HERE, w, w->web->interp);
    ok = 0;
  }

  if (w->web->session.isInitialized) {
    if (!WtSessionEndRequest(&w->web->session, interp)) {
      WtInterpError(HERE, w, w->web->interp);
      ok = 0;
    }
  }

  /* Interp delete script */

  if (!w->web->isPersistent || w->web->deleteInterp) {
    if (!WtEvalEventHandlers(&w->web->ctxEventHandlers, "deleteInterp",
        interp, w)) {
      ok = 0;
    }
  }

  /* Rest interp script *

  if (ok && w->web->isPersistent && !w->web->deleteInterp) {
    if (!WtEvalEventHandlers(&w->web->ctxEventHandlers, "restInterp",
        interp, w)) {
      ok = 0;
    }
  }

  /* Need to consume the client input (?) */

  if (!WtParseRequest(w, w->web->interp)) {
    WtInterpError(HERE, w, w->web->interp);
    w->web->terminate = 1;
    ok = 0;
  }

  Tcl_SetObjResult(interp, WtNewBool(ok));

  return TCL_OK;
}

int WtInitInterpCmd(ClientData clientData, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[])
{
  WtInitInterpCmdInternal(objv, objc, interp);
  return TCL_OK;
}

int WtInitPackagesCmd(ClientData clientData, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[])
{
  WtInitPackagesCmdInternal(objv, objc, interp);
  return TCL_OK;
}

int WtStartRequestCmd(ClientData clientData, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[])
{
  WtStartRequestCmdInternal(objv, objc, interp);
  return TCL_OK;
}

int WtEndRequestCmd(ClientData clientData, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[])
{
  WtEndRequestCmdInternal(objv, objc, interp);
  return TCL_OK;
}
